#!/usr/bin/env python
# -*- coding: utf-8 -*-
# =================================================
# @File    : parse_api_driver
# @IDE     : PyCharm
# @Author  : Liu Xian Gui
# @Date    : 2024/3/16 14:39
# @Describe: 
# ==================================================
import json
import re

import allure
from jsonpath import jsonpath
from library.log import logger
from library.tools.json_driver import JsonDriver

from library.tools.cache_driver import cacheClassDriver, cacheFileDriver
from library.models import ModelAPICase, ModelAssert, ModelTestCase, ModelAssertDataType
from library.base.baseapi import BaseAPI
from library.assertion.assert_driver import AssertDriver
from library.base.mysqldriver import MysqlDriver


class YamlApiDriver(JsonDriver):

    @classmethod
    def run_yaml_api(cls, case_data: ModelTestCase):
        for action_item in case_data.actions:
            # 动作模型
            action_data = ModelAPICase(**action_item)
            # 完善api请求参数
            if action_data.data:
                action_data.data = cls.fill_json_params(action_data.data)
            action_data.headers = cls.fill_json_params(action_data.headers)
            with allure.step(action_data.step):
                api = cls.create_run_api(describe=action_data.describe,
                                         method=action_data.method,
                                         host=action_data.host,
                                         url=action_data.url,
                                         files=action_data.files,
                                         headers=action_data.headers,
                                         data=action_data.data,
                                         proxies=action_data.proxies,
                                         is_json=action_data.is_json
                                         )
                response = api.run()
                logger.info(cls.json_pretty_print(action_data.assertion), attach_name="断言")
                logger.debug(action_data.sava_data_key_value)
                # 判断响应内容的类型
                content_type = response.headers["Content-Type"]
                response.encoding = response.apparent_encoding
                if "json" in content_type:
                    response_result = response.json()
                    logger.info(cls.json_pretty_print(response_result), attach_name="响应体")
                elif "text" in content_type or "html" in content_type or "plain" in content_type:
                    response_result = response.text
                    # logger.info(response_result, attach_name="响应体")
                # test-------
                # response_result = {"name": "abc", "age": 12}
                # 断言
                if action_data.assertion:
                    cls.assert_response_function(response_result, action_data.assertion)
                # 缓存值
                if action_data.sava_data_key_value:
                    cls.parse_sava_data(action_data.sava_data_key_value, response_result)
    @classmethod
    def parse_sava_data(cls, sava_data: dict, response):
        for key, value in sava_data.items():
            if isinstance(value, str):
                if value.startswith("jsonpath="):
                    res_value = jsonpath(response, value.split('=')[1])[0]
                elif value.startswith("sql="):
                    res_value = MysqlDriver().query(value.split("=")[1])
                else:
                    res_value = value
            else:
                res_value = value
            cacheClassDriver.set_data(key, res_value)

    @classmethod
    def assert_response_function(cls, response_result, assertions: list):
        assertion_list = [ModelAssert(**one) for one in assertions]
        for assertion in assertion_list:
            # 检查值-----------------------------------
            if not isinstance(assertion.check_value, str):
                check_value = assertion.check_value
            else:
                if "len(" in assertion.check_value:
                    check_value_var = cls.get_len_data(assertion.check_value)
                    check_value = len(cls.parse_check_or_expect_value(check_value_var, response_result))
                else:
                    check_value = cls.parse_check_or_expect_value(assertion.check_value, response_result)
            # 预期值-----------------------------------
            if not isinstance(assertion.expect_value, str):
                expect_value = assertion.expect_value
            else:
                if "len(" in assertion.expect_value:
                    expect_value_var = cls.get_len_data(assertion.expect_value)
                    expect_value = len(cls.parse_check_or_expect_value(expect_value_var, response_result))
                else:
                    expect_value = cls.parse_check_or_expect_value(assertion.expect_value, response_result)
            AssertDriver.assert_driver(check_value, assertion.assert_type, expect_value, "Assert Failed")

    @classmethod
    def parse_check_or_expect_value(cls, value, response=None):
        """
        解析断言值
        :param value: 检查值/预期值
        :param response: 响应json
        :return:
        """
        if isinstance(value, str):
            if value.startswith(ModelAssertDataType.jsonpath):
                res_value = jsonpath(response, value.split('=')[1])[0]
            elif value.startswith(ModelAssertDataType.sql):
                res_value = MysqlDriver().query(sql=value.split('=')[1])
                logger.info(res_value, "数据库查询结果")
            elif value.startswith(ModelAssertDataType.cache):
                res_value = cacheClassDriver.get_data(value.split('=')[1])
            else:
                res_value = value
        else:
            res_value = value
        return res_value

    @classmethod
    def get_len_data(cls, value):
        return re.findall("len\((.*?)\)", value)[0]

    @classmethod
    def fill_json_params(cls, case_json) -> dict:
        """
        填充json内容
        :param case_json: dic or list
        :return:
        """
        var_list = cls.find_json_variables(json.dumps(case_json))
        if len(var_list) == 0:
            return case_json
        for var in var_list:
            for json_path in cls.find_jsonpath_by_value(case_json, "$(%s)" % var):
                json_path = "$." + json_path
                if cacheClassDriver.get_data(var):
                    cls.update_value_by_jsonpath(case_json, json_path, cacheClassDriver.get_data(var))
                else:
                    if cacheFileDriver.get_cache(var):
                        cls.update_value_by_jsonpath(case_json, json_path, cacheFileDriver.get_cache(var))
                    else:
                        raise ValueError(f"variable [ {var} ] not found, please check yaml !")
        return case_json

    @classmethod
    def create_run_api(cls, describe, method, host, url, files, headers, data, proxies, is_json):
        api = BaseAPI()
        api.describe = describe
        api.method = method
        api.host = host
        api.url = url
        api.files = files
        api.headers = headers
        api.data = data
        api.proxies = proxies
        api.isJson = is_json
        return api

    @classmethod
    def create_query_database_step(cls):
        ...